Introduction + Methods
This is the 3rd time I’m trying to estimate the needed sample size
for our study. At this point, you should (hopefully) know the
drill…
This time we will try to estimate our needed N with another simulation
approach using deviance to a “true score” which we will get with
sampling a lot.
We use this sample sequence:
sample_sequence
## [1] 20 30 40 50 60 70 80 90 100 150 200 250 500 750 1000
## [16] 1250 1500 1750 2000 2250 2500 2750 3000 5000
And for each of these sample sizes, we simulate 400
times (while higher numbers would lead to a more robust/ reliable
finding, I still need to make sure that it can still be computable)…
Here are our assumed population distribution, which we will sample
from again:
normal <- round(rnorm(n_population, mean = 9,sd = 1.2))
normal <- pmin(pmax(normal, min_likert), max_likert)
strong_pol <- round(rbeta(n_population, shape1 = .1, shape2 = .1)*10) +1
strong_pol <- pmin(pmax(strong_pol, min_likert), max_likert)
rare <- c(round(rnorm(n_population *(1- prop_minority), mean = 2,sd = 1.3)),
round(rnorm(n_population *prop_minority, mean = 11,sd = .5)))
rare <- pmin(pmax(rare, min_likert), max_likert)
normal_2 <- round(rnorm(n_population, mean = 8,sd = 1))
normal_2 <- pmin(pmax(normal_2, min_likert), max_likert)
Pop_df <- data.frame(`Normal Distribution`= normal,
`Normal Distribution 2` = normal_2,
`Rare Polarization` = rare,
`Strong Polarization` = strong_pol)
names(Pop_df) <- c("None", "None 2", "Rare", "Strong Pol.")
Pop_df_plot <- Pop_df %>% pivot_longer(everything(), values_to = "Rating", names_to = "Distr") %>%
mutate(Distr = factor(Distr, levels = c("None", "None 2","Rare", "Strong Pol.")))
Pop_df_plot %>% ggplot(aes(Rating, fill = Distr))+
geom_bar(width= .75)+
facet_grid(rows = vars(Distr))+
scale_x_continuous(n.breaks = 11, expand = c(0,0))+
theme_minimal()+
theme(strip.text = element_blank(),
axis.text.y = element_blank(),
legend.position = "none",
plot.margin = margin(1, 2, 0, 0),
axis.text.x = element_text(size = 12))+
geom_text(aes(x=6,y = 3500, label= Distr, color = Distr), size = 6.8)+
scale_fill_manual(values = colors)+
scale_color_manual(values = colors)+
ylab("Count")

# max_cols <- max(sampled)
#
# # Register parallel backend with the desired number of cores
# num_cores <- detectCores()-1
#
# cl <- makeCluster(num_cores)
# registerDoParallel(cl)
#
# #rotate Pop_df, so our function works (each row needs to be a different distribution, instead of each col)
# rot_Pop_df <- as.data.frame(t(Pop_df))
#
# risk_dist_tr <- data.frame(risk_distribution = 1:4,
# risk_dist_transl = rownames(rot_Pop_df))
#
# # Define function to process each combination of risk_distribution, samplesize and replications per setting
# sample_and_replicate_for_all_risks <- function(i) {
# sampled_matrix_list <- list()
#
# for (j in 1:nrow(rot_Pop_df)) {
# mat <- replicate(replications_per_setting,
# sample(1:n_population, size = sampled[i, 1], replace = TRUE)) #create matrix of our samples with replacing, times n - replications
#
# sampled_table <- rot_Pop_df[j, mat] #using the matrix, collect the values from our risk distribution matrix (as a vector though...)
# sampled_matrix <- as.data.frame(matrix(sampled_table,
# nrow = replications_per_setting,
# ncol = sampled[i, 1],
# byrow = TRUE,
# dimnames = NULL)) #create df out of these vectors instead of flat vectors
#
#
# if (ncol(sampled_matrix) < max_cols) {
# padding_matrix <- matrix(NA,
# nrow = nrow(sampled_matrix),
# ncol = max_cols - ncol(sampled_matrix))
# sampled_matrix <- cbind(sampled_matrix, padding_matrix)
# } #if matrix is not wide enough for our end result matrix, padd it with NA columns, so binding rows is doable (needs same amount of ncols)
#
# sampled_matrix <- cbind(matrix(sampled[i,1], nrow = replications_per_setting),
# matrix(j, nrow = replications_per_setting),
# sampled_matrix) #bind columns with additional information such as sample size and which risk_distribution was sampled
#
# colnames(sampled_matrix) <- c("sample_size", "risk_distribution", paste0("rating_", 1:max_cols)) #rewrite colnames so it is identical to the bigh matrix
# sampled_matrix_list[[j]] <- sampled_matrix #store in list
# }
# return(do.call(rbind, sampled_matrix_list)) #after all risk distributions are sampled from, bind them all and return the output
#
# }
#
# # Perform parallel processing using foreach, iterating through the different samplesizes
# result <- foreach(i = 1:nrow(sampled), .combine = rbind) %dopar% {
# sample_and_replicate_for_all_risks(i)
# }
#
# # Stop the parallel backend
# stopCluster(cl)
#
# result <- mutate_all(result, as.numeric)
#
#
# vis_miss(result[,seq(from =3, to = ncol(result), length.out =20)], show_perc_col = F)
#taken from my previous work
# calc_bimodality_coefficient <- function(vec){
# skew <- skew(vec, na.rm = TRUE, type = 3)
# kurtosis <- kurtosi(vec, na.rm = TRUE, type = 3)
# n <- sum(!is.na(vec))
# return((skew^2+1) / (kurtosis + ((3*((n-1))^2)/((n-2)*(n-3))) ))
# }
#
# # Created this one "from scratch", as we work with a smaller scale of 11 instead of 101, the calculation of polarization does not take too long anymore
#
# calc_polarization <- function(vec){
# vec2 <- as.vector(vec)
# freq_vec <- agrmt::collapse(vec2, pos = c(1:11))
# return(agrmt::polarization(freq_vec))
# }
#
# # this code is commented, as the computation takes too long, and I hate waiting while kniting. I have saved a sepparate rds file, which will be read in instead. Readers who want to uncomment section, select the lines to uncomment and press Ctrl + Shift + C (on Windows/Linux) or Cmd + Shift + C (on macOS)
# #
# # #apply the functions to our result matrix, therefore calculate for each drawn sample the polarization metrics
# BC_result <- apply(result[,-c(1:2)], 1, calc_bimodality_coefficient)
# sum(is.na(BC_result))
#
#
# # Register parallel backend with the desired number of cores
# num_cores <- detectCores()-1
#
# cl <- makeCluster(num_cores)
# registerDoParallel(cl)
#
# # Perform parallel processing using foreach, iterating through the different drawn samples
# polarization_result <- foreach(i = 1:nrow(result), .combine = rbind) %dopar% {
# calc_polarization(result[i, -c(1:2)])
# }
# sum(is.na(polarization_result))
#
# # Stop the parallel backend
# stopCluster(cl)
#
#
# combined_result_measures <- cbind(polarization_result,
# BC_result,
# result[,1:2]
# )
# sum(is.na(combined_result_measures))
#
#
# combined_result_measures <- combined_result_measures %>%
# left_join(risk_dist_tr, by = "risk_distribution") %>%
# mutate(fsample_size = factor(sample_size, ordered = T))
#
# saveRDS(combined_result_measures, "saved_RDS\\combined_result_measures_power_analysis_deviance.rds")
combined_result_measures <- readRDS("saved_RDS\\combined_result_measures_power_analysis_deviance.rds")
vis_miss(combined_result_measures, sort_miss = F)

Results
In the following density plots, we see the distribution of each
calculated polarization measure (from the book
“Triggerpunkte” from Mau et al.), which ranges from 0 (not polarized,
people are in agreement) to 1 (highest polarization). We can see that
the more we sample, the more the results converge to a “true” score for
said assumed population distribution.
combined_result_measures %>%
ggplot(aes(polarization_result))+
geom_density(aes(fill = fsample_size), alpha = .4)+
facet_grid(rows =vars(risk_dist_transl))

And for the bimodality Coefficient as well:
combined_result_measures %>%
ggplot(aes(BC_result))+
geom_density(aes(fill = fsample_size), alpha = .4)+
facet_grid(rows =vars(risk_dist_transl))

In the following, we will calculate the mode of each
density plot (that is, where the polarization measures converges or has
a high probability that it ends up on X, if we were to sample it
fewer times). We can then plot these findings, and should look
at the point where the modes converges towards the
“true score” (in our case, the sample size of 5000).
combined_result_measures <- combined_result_measures %>%
mutate(grouped_distXsample = paste(risk_dist_transl, fsample_size, sep = "_"))
splitted <- split(combined_result_measures$polarization_result, f = combined_result_measures$grouped_distXsample)
splitted2 <- split(combined_result_measures$BC_result, f = combined_result_measures$grouped_distXsample)
mode_dens_res <- data.frame()
for (i in 1:length(splitted)) {
dens_res <- density(splitted[[i]])
dens_res2 <- density(splitted2[[i]])
dens_df <- data.frame(group = names(splitted)[i],
mode_dens_pol = dens_res$x[which.max(dens_res$y)],
mode_dens_BC = dens_res2$x[which.max(dens_res2$y)])
mode_dens_res <- rbind(mode_dens_res, dens_df)
}
cleaned_mode_dens_res <- mode_dens_res %>%
separate(group, sep = "_", into = c("Distribution", "fsample_size")) %>%
mutate(Sample_Size = as.integer(fsample_size)) %>%
pivot_longer(cols = starts_with("mode_dens"),
names_prefix = "mode_dens_",
names_to = "measure",
values_to = "value")
cleaned_mode_dens_res %>%
ggplot(aes(Sample_Size, value, group = Distribution, col = Distribution))+
geom_line(size= 1.1)+
facet_grid(rows = vars(measure))+
scale_x_continuous(breaks = sample_sequence)+
theme_minimal()+
scale_color_manual(values = colors)

Probability of Sample being in True
Score Interval
While the mode could a be good indicator, one might also argue that
it is more relevant to have a good chance of capturing the true
polarization value. That is, the probability that the measure is only
off by only .1 (meaning it is always within a interval of + 0.05 and -
0.05) from the true score. So let’s calculate the % where the
400 simulations per sample size was within this interval.
One could then interpret the results as: the probability of
coming to the same polarization value (with an interval of .1) compared
to if we had sampled 5’000 people.
Interval of .1
# calculate the "true score" value of said polarization measure for each population distribution
true_score <- combined_result_measures %>%
filter(sample_size == 5000) %>%
group_by(risk_dist_transl) %>%
summarize(mean_true_score_BC = mean(BC_result),
mean_true_score_pol = mean(polarization_result))
combined_result_measures_within_interval <- combined_result_measures %>%
left_join(true_score, by = c("risk_dist_transl")) %>%
mutate(within_BC = if_else(BC_result >= mean_true_score_BC- lower_and_upper_interval_limit &
BC_result <= mean_true_score_BC + lower_and_upper_interval_limit, 1, 0),
within_pol = if_else(polarization_result >= mean_true_score_pol- lower_and_upper_interval_limit &
polarization_result <= mean_true_score_pol + lower_and_upper_interval_limit, 1, 0))
summarized_within_interval <-
combined_result_measures_within_interval %>%
group_by(risk_dist_transl, sample_size) %>%
summarize(count_within_BC = sum(within_BC),
count_within_pol = sum(within_pol),
perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
summarized_within_interval %>% ggplot(aes(factor(sample_size), percentage, col = risk_dist_transl, group = risk_dist_transl))+
geom_point()+
geom_line()+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.05 interval of true score",
x = "sample size",
title = "Interval of +- 0.05")+
scale_color_manual(values = colors)+
theme(legend.position = "none")

Interval of .05
As the interval of .05 (which results of a coverage of .1) was
“arbitrarily” taken, let’s use the same procedure for an interval of
.025 (or a span of only 0.05). In this case, one would expect that we
need a bigger sample size, as the margin for “error” is smaller where we
would be confident enough that we have captured the true score.
combined_result_measures_within_interval <- combined_result_measures %>%
left_join(true_score, by = c("risk_dist_transl")) %>%
mutate(within_BC = if_else(BC_result >= mean_true_score_BC- lower_and_upper_interval_limit2 &
BC_result <= mean_true_score_BC + lower_and_upper_interval_limit2, 1, 0),
within_pol = if_else(polarization_result >= mean_true_score_pol- lower_and_upper_interval_limit2 &
polarization_result <= mean_true_score_pol + lower_and_upper_interval_limit2, 1, 0))
summarized_within_interval <-
combined_result_measures_within_interval %>%
group_by(risk_dist_transl, sample_size) %>%
summarize(count_within_BC = sum(within_BC),
count_within_pol = sum(within_pol),
perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
summarized_within_interval %>% ggplot(aes(factor(sample_size), percentage, col = risk_dist_transl, group = risk_dist_transl))+
geom_point()+
geom_line()+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.025 interval of true score",
x = "sample size",
title = "Interval of +- 0.025")+
scale_color_manual(values = colors)+
theme(legend.position = "none")

Difference between two
polarizations
The approach above would only tell us how likely it is that the
sampled distribution would lead to almost the same polarization value if
one had sampled 5’000 people (or in a sense, a “true score”, as it
converges to said population distribution). In the following section, we
will go one step further and analyse how likely it is to find the same
difference between two population distributions and two
sampled distributions.
true_score_long <- true_score %>%
pivot_longer(cols = starts_with("mean_true_score"),
names_prefix = "mean_true_score_",
names_to = "measure",
values_to = "value")
true_score_expanded <- expand.grid(x = true_score$risk_dist_transl, y =true_score$risk_dist_transl) %>%
filter(x != y) %>%
distinct() %>%
left_join(true_score, by = c("x" = "risk_dist_transl")) %>%
left_join(true_score, by = c("y" = "risk_dist_transl"), suffix = c("_x", "_y")) %>%
mutate(true_diff_BC = mean_true_score_BC_x - mean_true_score_BC_y,
true_diff_pol = mean_true_score_pol_x - mean_true_score_pol_y)
#because we have duplicate combinations (e.g. abs(x - y) == abs(y - x)), I'll sort them and take half of it (e.g. only the neccessary combinations)
n_unique <- nrow(true_score) * (nrow(true_score)-1) / 2
true_score_unique <- true_score_expanded %>%
arrange(desc(true_diff_BC)) %>%
slice_head(n = n_unique) %>%
select(x, y, true_diff_BC, true_diff_pol)
true_score_unique %>%
kable(digits = 2)
| Strong Pol. |
None 2 |
0.54 |
0.70 |
| Strong Pol. |
None |
0.50 |
0.68 |
| Rare |
None 2 |
0.42 |
0.06 |
| Rare |
None |
0.38 |
0.04 |
| Strong Pol. |
Rare |
0.12 |
0.64 |
| None |
None 2 |
0.04 |
0.02 |
These are the true score differences between two distributions if one
were to average the difference from sampling 5000 ratings for each
population.
In the following, we will calculate the difference between the
calculated polarization measures from another distribution. Again, we
will take two intervals: .1 and .05 (that is, the sampled difference is
allowed to deviate by .05 or .025 from the true score on either
side).
Interval of .1
combined_result_measures_selected <- combined_result_measures %>%
select(contains("result"), sample_size, risk_dist_transl) %>%
group_by(risk_dist_transl, sample_size) %>%
mutate(sample_ID = row_number())
diff_distributions <- true_score_unique %>%
left_join(combined_result_measures_selected, by = c("x" = "risk_dist_transl")) %>%
left_join(combined_result_measures_selected, by = c("y" = "risk_dist_transl", "sample_size", "sample_ID"), suffix = c("_x", "_y")) %>%
mutate(diff_BC = BC_result_x - BC_result_y,
diff_pol = polarization_result_x - polarization_result_y)
within_interval_diff_distributions <- diff_distributions %>%
mutate(within_BC = if_else(true_diff_BC >= diff_BC - lower_and_upper_interval_limit &
true_diff_BC <= diff_BC + lower_and_upper_interval_limit, 1, 0),
within_pol = if_else(true_diff_pol >= diff_pol - lower_and_upper_interval_limit &
true_diff_pol <= diff_pol + lower_and_upper_interval_limit, 1, 0),
comparison = paste(x, y, sep = " - ")
) %>%
group_by(comparison, sample_size) %>%
summarize(perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
within_interval_diff_distributions %>% ggplot(aes(factor(sample_size), percentage, col = factor(comparison), group = factor(comparison)))+
geom_point(size = 2)+
geom_line(linewidth = 1.2)+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.05 interval of true score",
x = "sample size",
title = "Interval of +- 0.05",
color = "Comparison between")+
scale_color_manual(values = colors2)+
theme(legend.spacing = unit(0.1, "cm"),
legend.margin = margin(0,0,0,0),
legend.box.margin = margin(0,0,0,0),
legend.position = c(.5,.05),
legend.direction = "horizontal")

Interval of .05
within_interval_diff_distributions <- diff_distributions %>%
mutate(within_BC = if_else(true_diff_BC >= diff_BC - lower_and_upper_interval_limit2 &
true_diff_BC <= diff_BC + lower_and_upper_interval_limit2, 1, 0),
within_pol = if_else(true_diff_pol >= diff_pol - lower_and_upper_interval_limit2 &
true_diff_pol <= diff_pol + lower_and_upper_interval_limit2, 1, 0),
comparison = paste(x, y, sep = " - ")
) %>%
group_by(comparison, sample_size) %>%
summarize(perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
within_interval_diff_distributions %>% ggplot(aes(factor(sample_size), percentage, col = factor(comparison), group = factor(comparison)))+
geom_point(size = 2)+
geom_line(linewidth = 1.2)+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.025 interval of true score",
x = "sample size",
title = "Interval of +- 0.025",
color = "Comparison between")+
scale_color_manual(values = colors2)+
theme(legend.spacing = unit(0.1, "cm"),
legend.margin = margin(0,0,0,0),
legend.box.margin = margin(0,0,0,0),
legend.position = c(.5,.05),
legend.direction = "horizontal")

LS0tDQp0aXRsZTogIlJpc2sgUG9sYXJpemF0aW9uIFBvd2VyIEFuYWx5c2lzIHVzaW5nIERldmlhbmNlIg0KYXV0aG9yOiAiQW5keSBDYW8iDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IA0KICAgaHRtbF9kb2N1bWVudDoNCiAgICAgIGNzczogc3R5bGVzLmNzcw0KICAgICAgdG9jOiB0cnVlDQogICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlDQogICAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQ0KICAgICAgc21vb3RoX2NvbnRyb2xsOiBmYWxzZQ0KICAgICAgZmlnLndpZHRoOiAyNg0KICAgICAgZmlnLmhlaWdodDogMjYNCiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCiMgSW50cm9kdWN0aW9uICsgTWV0aG9kcw0KDQpgYGB7ciBTZXR1cCwgaW5jbHVkZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgI2RhdGEgd3JhbmdsaW5nIGFuZCBvdGhlciB0b29scyBmb3IgUg0KbGlicmFyeShrbml0cikgIyByZXBvcnQgZ2VuZXJhdGlvbiBpbiBSDQpsaWJyYXJ5KHBzeWNoKSAjY2FsY3VsYXRlIHNrZXcgYW5kIGt1cnRvc2lzIGZvciBCQw0KbGlicmFyeShhZ3JtdCkgI2ZvciBhZ3JlZW1lbnQgYW5kIHBvbGFyaXphdGlvbiBjYWxjdWxhdGlvbg0KbGlicmFyeSh2aXNkYXQpICN2aXN1YWxpemUgZGF0YWZyYW1lcyBpbiBwbG90cw0KbGlicmFyeShSQ29sb3JCcmV3ZXIpICNlYXN5IHRvIHVzZSBjb2xvciBwYWxldHRlcw0KbGlicmFyeShybWFya2Rvd24pICNmb3IgdGhlIHBhZ2VkX3RhYmxlIGZ1bmN0aW9uDQpsaWJyYXJ5KGRvUGFyYWxsZWwpICNwYXJhbGxlbCBjb21wdXRhdGlvbiB1c2luZyBtdWx0aXBsZSBjb3Jlcw0KbGlicmFyeShmb3JlYWNoKSAjIGZvciBlYWNoIGZ1bmN0aW9uLCBzbyB0aGUgc2ltdWxhdGlvbiBkb2VzIG5vdCB0YWtlIGFnZXMNCg0KDQpsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQgPC0gLjA1DQpsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyIDwtIC4wMjUNCg0KY29sb3JzIDwtIGJyZXdlci5wYWwoNCwgIkRhcmsyIikNCmNvbG9yczIgPC0gYnJld2VyLnBhbCg2LCAiU2V0MiIpDQoNCm5fc2NhbGVfb25fbGlrZXJ0IDwtIGMoMToxMSkNCm1pbl9saWtlcnQgPC0gbWluKG5fc2NhbGVfb25fbGlrZXJ0KQ0KbWF4X2xpa2VydCA8LSBtYXgobl9zY2FsZV9vbl9saWtlcnQpDQoNCnNhbXBsZV9zZXF1ZW5jZSA8LSBjKDIwLDMwLDQwLDUwLCA2MCwgNzAsIDgwLCA5MCwgMTAwLCAxNTAsIDIwMCxzZXEoMjUwLCAzMDAwLCBieSA9IDI1MCksIDUwMDApDQpzYW1wbGVkIDwtIGRhdGEuZnJhbWUoTl9wYXJ0ID0gc2FtcGxlX3NlcXVlbmNlKQ0KDQpyZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgPC0gNDAwDQoNCm5fcG9wdWxhdGlvbiA8LSAxMF40DQpwcm9wX21pbm9yaXR5IDwtIC4wNQ0KDQojc2V0IHNlZWQgc28gZXZlcnkgcmFuZG9tIHRoaW5nIGhlcmUgaXMgcmVwcm9kdWNpYmxlDQpzZXQuc2VlZCg0MikNCg0KI3NldCBlY2hvID0gRkFMU0UgKGUuZy4gZG9udCBzaG93IGNvZGUgaW4gb3V0cHV0KSBmb3IgYWxsIGNodW5rcywgZXhjZXB0IHdoZW4gZXhwbGljaXRseSB0ZWxsaW5nIG90aGVyd2lzZQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICAgZWNobyA9IFRSVUUsDQogICB3YXJuaW5nID0gRkFMU0UsDQogICBtZXNzYWdlID0gRkFMU0UNCiAgICkNCmBgYA0KDQpUaGlzIGlzIHRoZSAzcmQgdGltZSBJJ20gdHJ5aW5nIHRvIGVzdGltYXRlIHRoZSBuZWVkZWQgc2FtcGxlIHNpemUgZm9yIG91ciBzdHVkeS4gQXQgdGhpcyBwb2ludCwgeW91IHNob3VsZCAoaG9wZWZ1bGx5KSBrbm93IHRoZSBkcmlsbC4uLiAgIA0KVGhpcyB0aW1lIHdlIHdpbGwgdHJ5IHRvIGVzdGltYXRlIG91ciBuZWVkZWQgTiB3aXRoIGFub3RoZXIgc2ltdWxhdGlvbiBhcHByb2FjaCB1c2luZyBkZXZpYW5jZSB0byBhICJ0cnVlIHNjb3JlIiB3aGljaCB3ZSB3aWxsIGdldCB3aXRoIHNhbXBsaW5nIGEgbG90Lg0KDQpXZSB1c2UgdGhpcyBzYW1wbGUgc2VxdWVuY2U6ICANCmBgYHtyfQ0Kc2FtcGxlX3NlcXVlbmNlDQpgYGANCg0KQW5kIGZvciBlYWNoIG9mIHRoZXNlIHNhbXBsZSBzaXplcywgd2Ugc2ltdWxhdGUgYGByIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZ2BgIHRpbWVzICh3aGlsZSBoaWdoZXIgbnVtYmVycyB3b3VsZCBsZWFkIHRvIGEgbW9yZSByb2J1c3QvIHJlbGlhYmxlIGZpbmRpbmcsIEkgc3RpbGwgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCBpdCBjYW4gc3RpbGwgYmUgY29tcHV0YWJsZSkuLi4NCg0KSGVyZSBhcmUgb3VyIGFzc3VtZWQgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24sIHdoaWNoIHdlIHdpbGwgc2FtcGxlIGZyb20gYWdhaW46ICANCg0KYGBge3IgR2VuZXJhdGlvbiBvZiBQb3B1bGF0aW9uIERpc3RyaWJ1dGlvbn0NCg0Kbm9ybWFsIDwtIHJvdW5kKHJub3JtKG5fcG9wdWxhdGlvbiwgbWVhbiA9IDksc2QgPSAxLjIpKQ0Kbm9ybWFsIDwtIHBtaW4ocG1heChub3JtYWwsIG1pbl9saWtlcnQpLCBtYXhfbGlrZXJ0KQ0KDQpzdHJvbmdfcG9sIDwtIHJvdW5kKHJiZXRhKG5fcG9wdWxhdGlvbiwgc2hhcGUxID0gLjEsIHNoYXBlMiA9IC4xKSoxMCkgKzENCnN0cm9uZ19wb2wgPC0gcG1pbihwbWF4KHN0cm9uZ19wb2wsIG1pbl9saWtlcnQpLCBtYXhfbGlrZXJ0KQ0KDQpyYXJlIDwtIGMocm91bmQocm5vcm0obl9wb3B1bGF0aW9uICooMS0gcHJvcF9taW5vcml0eSksIG1lYW4gPSAyLHNkID0gMS4zKSksIA0KICAgICAgICAgICByb3VuZChybm9ybShuX3BvcHVsYXRpb24gKnByb3BfbWlub3JpdHksIG1lYW4gPSAxMSxzZCA9IC41KSkpDQpyYXJlIDwtIHBtaW4ocG1heChyYXJlLCBtaW5fbGlrZXJ0KSwgbWF4X2xpa2VydCkNCg0Kbm9ybWFsXzIgPC0gcm91bmQocm5vcm0obl9wb3B1bGF0aW9uLCBtZWFuID0gOCxzZCA9IDEpKQ0Kbm9ybWFsXzIgPC0gcG1pbihwbWF4KG5vcm1hbF8yLCBtaW5fbGlrZXJ0KSwgbWF4X2xpa2VydCkNCg0KDQpQb3BfZGYgPC0gZGF0YS5mcmFtZShgTm9ybWFsIERpc3RyaWJ1dGlvbmA9IG5vcm1hbCwNCiAgICAgICAgICAgICAgICAgICAgIGBOb3JtYWwgRGlzdHJpYnV0aW9uIDJgID0gbm9ybWFsXzIsDQogICAgICAgICAgICAgICAgICAgICBgUmFyZSBQb2xhcml6YXRpb25gID0gcmFyZSwNCiAgICAgICAgICAgICAgICAgICAgIGBTdHJvbmcgUG9sYXJpemF0aW9uYCA9IHN0cm9uZ19wb2wpDQoNCm5hbWVzKFBvcF9kZikgPC0gYygiTm9uZSIsICJOb25lIDIiLCAiUmFyZSIsICJTdHJvbmcgUG9sLiIpDQoNCg0KDQpQb3BfZGZfcGxvdCA8LSBQb3BfZGYgJT4lIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIHZhbHVlc190byA9ICJSYXRpbmciLCBuYW1lc190byA9ICJEaXN0ciIpICU+JSANCiAgIG11dGF0ZShEaXN0ciA9IGZhY3RvcihEaXN0ciwgbGV2ZWxzID0gYygiTm9uZSIsICJOb25lIDIiLCJSYXJlIiwgIlN0cm9uZyBQb2wuIikpKQ0KDQpQb3BfZGZfcGxvdCAlPiUgZ2dwbG90KGFlcyhSYXRpbmcsIGZpbGwgPSBEaXN0cikpKw0KICAgZ2VvbV9iYXIod2lkdGg9IC43NSkrDQogICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKERpc3RyKSkrDQogICBzY2FsZV94X2NvbnRpbnVvdXMobi5icmVha3MgPSAxMSwgZXhwYW5kID0gYygwLDApKSsNCiAgIHRoZW1lX21pbmltYWwoKSsNCiAgIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDIsIDAsIDApLA0KICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkrDQogICBnZW9tX3RleHQoYWVzKHg9Nix5ID0gMzUwMCwgbGFiZWw9IERpc3RyLCBjb2xvciA9IERpc3RyKSwgc2l6ZSA9IDYuOCkrDQogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpKw0KICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykrDQogICB5bGFiKCJDb3VudCIpDQpgYGANCg0KYGBge3IgU2FtcGxpbmcsIGNhY2hlPVRSVUV9DQojIG1heF9jb2xzIDwtIG1heChzYW1wbGVkKQ0KIyANCiMgIyBSZWdpc3RlciBwYXJhbGxlbCBiYWNrZW5kIHdpdGggdGhlIGRlc2lyZWQgbnVtYmVyIG9mIGNvcmVzDQojIG51bV9jb3JlcyA8LSBkZXRlY3RDb3JlcygpLTENCiMgDQojIGNsIDwtIG1ha2VDbHVzdGVyKG51bV9jb3JlcykNCiMgcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQ0KIyANCiMgI3JvdGF0ZSBQb3BfZGYsIHNvIG91ciBmdW5jdGlvbiB3b3JrcyAoZWFjaCByb3cgbmVlZHMgdG8gYmUgYSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9uLCBpbnN0ZWFkIG9mIGVhY2ggY29sKQ0KIyByb3RfUG9wX2RmIDwtIGFzLmRhdGEuZnJhbWUodChQb3BfZGYpKQ0KIyANCiMgcmlza19kaXN0X3RyIDwtIGRhdGEuZnJhbWUocmlza19kaXN0cmlidXRpb24gPSAxOjQsDQojICAgICAgICAgICAgcmlza19kaXN0X3RyYW5zbCA9IHJvd25hbWVzKHJvdF9Qb3BfZGYpKQ0KIyANCiMgIyBEZWZpbmUgZnVuY3Rpb24gdG8gcHJvY2VzcyBlYWNoIGNvbWJpbmF0aW9uIG9mIHJpc2tfZGlzdHJpYnV0aW9uLCBzYW1wbGVzaXplIGFuZCByZXBsaWNhdGlvbnMgcGVyIHNldHRpbmcNCiMgc2FtcGxlX2FuZF9yZXBsaWNhdGVfZm9yX2FsbF9yaXNrcyA8LSBmdW5jdGlvbihpKSB7DQojICAgc2FtcGxlZF9tYXRyaXhfbGlzdCA8LSBsaXN0KCkNCiMgDQojICAgZm9yIChqIGluIDE6bnJvdyhyb3RfUG9wX2RmKSkgew0KIyAgICAgbWF0IDwtIHJlcGxpY2F0ZShyZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcsDQojICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZSgxOm5fcG9wdWxhdGlvbiwgc2l6ZSA9IHNhbXBsZWRbaSwgMV0sIHJlcGxhY2UgPSBUUlVFKSkgI2NyZWF0ZSBtYXRyaXggb2Ygb3VyIHNhbXBsZXMgd2l0aCByZXBsYWNpbmcsIHRpbWVzIG4gLSByZXBsaWNhdGlvbnMNCiMgDQojICAgICBzYW1wbGVkX3RhYmxlIDwtIHJvdF9Qb3BfZGZbaiwgbWF0XSAjdXNpbmcgdGhlIG1hdHJpeCwgY29sbGVjdCB0aGUgdmFsdWVzIGZyb20gb3VyIHJpc2sgZGlzdHJpYnV0aW9uIG1hdHJpeCAoYXMgYSB2ZWN0b3IgdGhvdWdoLi4uKQ0KIyAgICAgc2FtcGxlZF9tYXRyaXggPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgoc2FtcGxlZF90YWJsZSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gc2FtcGxlZFtpLCAxXSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gVFJVRSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gTlVMTCkpICNjcmVhdGUgZGYgb3V0IG9mIHRoZXNlIHZlY3RvcnMgaW5zdGVhZCBvZiBmbGF0IHZlY3RvcnMNCiMgDQojIA0KIyAgICAgaWYgKG5jb2woc2FtcGxlZF9tYXRyaXgpIDwgbWF4X2NvbHMpIHsNCiMgICAgICAgcGFkZGluZ19tYXRyaXggPC0gbWF0cml4KE5BLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IG5yb3coc2FtcGxlZF9tYXRyaXgpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IG1heF9jb2xzIC0gbmNvbChzYW1wbGVkX21hdHJpeCkpDQojICAgICAgIHNhbXBsZWRfbWF0cml4IDwtIGNiaW5kKHNhbXBsZWRfbWF0cml4LCBwYWRkaW5nX21hdHJpeCkNCiMgICAgIH0gI2lmIG1hdHJpeCBpcyBub3Qgd2lkZSBlbm91Z2ggZm9yIG91ciBlbmQgcmVzdWx0IG1hdHJpeCwgcGFkZCBpdCB3aXRoIE5BIGNvbHVtbnMsIHNvIGJpbmRpbmcgcm93cyBpcyBkb2FibGUgKG5lZWRzIHNhbWUgYW1vdW50IG9mIG5jb2xzKQ0KIyANCiMgICAgIHNhbXBsZWRfbWF0cml4IDwtIGNiaW5kKG1hdHJpeChzYW1wbGVkW2ksMV0sIG5yb3cgPSByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0cml4KGosIG5yb3cgPSByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlZF9tYXRyaXgpICNiaW5kIGNvbHVtbnMgd2l0aCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIHN1Y2ggYXMgc2FtcGxlIHNpemUgYW5kIHdoaWNoIHJpc2tfZGlzdHJpYnV0aW9uIHdhcyBzYW1wbGVkDQojIA0KIyAgICAgY29sbmFtZXMoc2FtcGxlZF9tYXRyaXgpIDwtIGMoInNhbXBsZV9zaXplIiwgInJpc2tfZGlzdHJpYnV0aW9uIiwgcGFzdGUwKCJyYXRpbmdfIiwgMTptYXhfY29scykpICNyZXdyaXRlIGNvbG5hbWVzIHNvIGl0IGlzIGlkZW50aWNhbCB0byB0aGUgYmlnaCBtYXRyaXgNCiMgICAgIHNhbXBsZWRfbWF0cml4X2xpc3RbW2pdXSA8LSBzYW1wbGVkX21hdHJpeCAjc3RvcmUgaW4gbGlzdA0KIyAgIH0NCiMgICByZXR1cm4oZG8uY2FsbChyYmluZCwgc2FtcGxlZF9tYXRyaXhfbGlzdCkpICNhZnRlciBhbGwgcmlzayBkaXN0cmlidXRpb25zIGFyZSBzYW1wbGVkIGZyb20sIGJpbmQgdGhlbSBhbGwgYW5kIHJldHVybiB0aGUgb3V0cHV0DQojIA0KIyB9DQojIA0KIyAjIFBlcmZvcm0gcGFyYWxsZWwgcHJvY2Vzc2luZyB1c2luZyBmb3JlYWNoLCBpdGVyYXRpbmcgdGhyb3VnaCB0aGUgZGlmZmVyZW50IHNhbXBsZXNpemVzDQojIHJlc3VsdCA8LSBmb3JlYWNoKGkgPSAxOm5yb3coc2FtcGxlZCksIC5jb21iaW5lID0gcmJpbmQpICVkb3BhciUgew0KIyAgIHNhbXBsZV9hbmRfcmVwbGljYXRlX2Zvcl9hbGxfcmlza3MoaSkNCiMgfQ0KIyANCiMgIyBTdG9wIHRoZSBwYXJhbGxlbCBiYWNrZW5kDQojIHN0b3BDbHVzdGVyKGNsKQ0KIyANCiMgcmVzdWx0IDwtIG11dGF0ZV9hbGwocmVzdWx0LCBhcy5udW1lcmljKQ0KIyANCiMgDQojIHZpc19taXNzKHJlc3VsdFssc2VxKGZyb20gPTMsIHRvID0gbmNvbChyZXN1bHQpLCBsZW5ndGgub3V0ID0yMCldLCBzaG93X3BlcmNfY29sID0gRikNCmBgYCAgDQoNCg0KYGBge3IgQ2FsY3VsYXRpbmcgUG9sYXJpc2F0aW9uIE1lYXN1cmVzLCBjYWNoZT1UUlVFfQ0KI3Rha2VuIGZyb20gbXkgcHJldmlvdXMgd29yaw0KDQojIGNhbGNfYmltb2RhbGl0eV9jb2VmZmljaWVudCA8LSBmdW5jdGlvbih2ZWMpew0KIyAgICBza2V3IDwtIHNrZXcodmVjLCBuYS5ybSA9IFRSVUUsIHR5cGUgPSAzKQ0KIyAgICBrdXJ0b3NpcyA8LSBrdXJ0b3NpKHZlYywgbmEucm0gPSBUUlVFLCB0eXBlID0gMykNCiMgICAgbiA8LSBzdW0oIWlzLm5hKHZlYykpDQojICAgIHJldHVybigoc2tld14yKzEpIC8gKGt1cnRvc2lzICsgKCgzKigobi0xKSleMikvKChuLTIpKihuLTMpKSkgKSkNCiMgfQ0KIyANCiMgIyBDcmVhdGVkIHRoaXMgb25lICJmcm9tIHNjcmF0Y2giLCBhcyB3ZSB3b3JrIHdpdGggYSBzbWFsbGVyIHNjYWxlIG9mIDExIGluc3RlYWQgb2YgMTAxLCB0aGUgY2FsY3VsYXRpb24gb2YgcG9sYXJpemF0aW9uIGRvZXMgbm90IHRha2UgdG9vIGxvbmcgYW55bW9yZQ0KIyANCiMgY2FsY19wb2xhcml6YXRpb24gPC0gZnVuY3Rpb24odmVjKXsNCiMgICAgdmVjMiA8LSBhcy52ZWN0b3IodmVjKQ0KIyAgICBmcmVxX3ZlYyA8LSBhZ3JtdDo6Y29sbGFwc2UodmVjMiwgcG9zID0gYygxOjExKSkNCiMgICAgcmV0dXJuKGFncm10Ojpwb2xhcml6YXRpb24oZnJlcV92ZWMpKQ0KIyB9DQojIA0KIyAjIHRoaXMgY29kZSBpcyBjb21tZW50ZWQsIGFzIHRoZSBjb21wdXRhdGlvbiB0YWtlcyB0b28gbG9uZywgYW5kIEkgaGF0ZSB3YWl0aW5nIHdoaWxlIGtuaXRpbmcuIEkgaGF2ZSBzYXZlZCBhIHNlcHBhcmF0ZSByZHMgZmlsZSwgd2hpY2ggd2lsbCBiZSByZWFkIGluIGluc3RlYWQuIFJlYWRlcnMgd2hvIHdhbnQgdG8gdW5jb21tZW50IHNlY3Rpb24sIHNlbGVjdCB0aGUgbGluZXMgdG8gdW5jb21tZW50IGFuZCBwcmVzcyBDdHJsICsgU2hpZnQgKyBDIChvbiBXaW5kb3dzL0xpbnV4KSBvciBDbWQgKyBTaGlmdCArIEMgKG9uIG1hY09TKQ0KIyAjDQojICMgI2FwcGx5IHRoZSBmdW5jdGlvbnMgdG8gb3VyIHJlc3VsdCBtYXRyaXgsIHRoZXJlZm9yZSBjYWxjdWxhdGUgZm9yIGVhY2ggZHJhd24gc2FtcGxlIHRoZSBwb2xhcml6YXRpb24gbWV0cmljcw0KIyBCQ19yZXN1bHQgPC0gYXBwbHkocmVzdWx0WywtYygxOjIpXSwgMSwgY2FsY19iaW1vZGFsaXR5X2NvZWZmaWNpZW50KQ0KIyBzdW0oaXMubmEoQkNfcmVzdWx0KSkNCiMgDQojIA0KIyAjIFJlZ2lzdGVyIHBhcmFsbGVsIGJhY2tlbmQgd2l0aCB0aGUgZGVzaXJlZCBudW1iZXIgb2YgY29yZXMNCiMgbnVtX2NvcmVzIDwtIGRldGVjdENvcmVzKCktMQ0KIyANCiMgY2wgPC0gbWFrZUNsdXN0ZXIobnVtX2NvcmVzKQ0KIyByZWdpc3RlckRvUGFyYWxsZWwoY2wpDQojIA0KIyAjIFBlcmZvcm0gcGFyYWxsZWwgcHJvY2Vzc2luZyB1c2luZyBmb3JlYWNoLCBpdGVyYXRpbmcgdGhyb3VnaCB0aGUgZGlmZmVyZW50IGRyYXduIHNhbXBsZXMNCiMgcG9sYXJpemF0aW9uX3Jlc3VsdCA8LSBmb3JlYWNoKGkgPSAxOm5yb3cocmVzdWx0KSwgLmNvbWJpbmUgPSByYmluZCkgJWRvcGFyJSB7DQojICAgY2FsY19wb2xhcml6YXRpb24ocmVzdWx0W2ksIC1jKDE6MildKQ0KIyB9DQojIHN1bShpcy5uYShwb2xhcml6YXRpb25fcmVzdWx0KSkNCiMgDQojICMgU3RvcCB0aGUgcGFyYWxsZWwgYmFja2VuZA0KIyBzdG9wQ2x1c3RlcihjbCkNCiMgDQojIA0KIyBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgPC0gY2JpbmQocG9sYXJpemF0aW9uX3Jlc3VsdCwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJDX3Jlc3VsdCwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdFssMToyXQ0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KIyBzdW0oaXMubmEoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzKSkNCiMgDQojIA0KIyBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgPC0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JQ0KIyAgICBsZWZ0X2pvaW4ocmlza19kaXN0X3RyLCBieSA9ICJyaXNrX2Rpc3RyaWJ1dGlvbiIpICU+JQ0KIyAgICBtdXRhdGUoZnNhbXBsZV9zaXplID0gZmFjdG9yKHNhbXBsZV9zaXplLCBvcmRlcmVkID0gVCkpDQojIA0KIyBzYXZlUkRTKGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcywgInNhdmVkX1JEU1xcY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3Bvd2VyX2FuYWx5c2lzX2RldmlhbmNlLnJkcyIpDQoNCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyA8LSByZWFkUkRTKCJzYXZlZF9SRFNcXGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc19wb3dlcl9hbmFseXNpc19kZXZpYW5jZS5yZHMiKQ0KDQoNCg0KdmlzX21pc3MoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzLCBzb3J0X21pc3MgPSBGKQ0KYGBgDQoNCiMgUmVzdWx0cw0KDQpJbiB0aGUgZm9sbG93aW5nIGRlbnNpdHkgcGxvdHMsIHdlIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGVhY2ggY2FsY3VsYXRlZCAqKnBvbGFyaXphdGlvbioqIG1lYXN1cmUgKGZyb20gdGhlIGJvb2sgIlRyaWdnZXJwdW5rdGUiIGZyb20gTWF1IGV0IGFsLiksIHdoaWNoIHJhbmdlcyBmcm9tIDAgKG5vdCBwb2xhcml6ZWQsIHBlb3BsZSBhcmUgaW4gYWdyZWVtZW50KSB0byAxIChoaWdoZXN0IHBvbGFyaXphdGlvbikuIFdlIGNhbiBzZWUgdGhhdCB0aGUgbW9yZSB3ZSBzYW1wbGUsIHRoZSBtb3JlIHRoZSByZXN1bHRzIGNvbnZlcmdlIHRvIGEgInRydWUiIHNjb3JlIGZvciBzYWlkIGFzc3VtZWQgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGdncGxvdChhZXMocG9sYXJpemF0aW9uX3Jlc3VsdCkpKw0KICAgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsID0gZnNhbXBsZV9zaXplKSwgYWxwaGEgPSAuNCkrDQogICBmYWNldF9ncmlkKHJvd3MgPXZhcnMocmlza19kaXN0X3RyYW5zbCkpDQpgYGANCg0KQW5kIGZvciB0aGUgYmltb2RhbGl0eSBDb2VmZmljaWVudCBhcyB3ZWxsOg0KDQpgYGB7cn0NCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyAlPiUgDQogICBnZ3Bsb3QoYWVzKEJDX3Jlc3VsdCkpKw0KICAgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsID0gZnNhbXBsZV9zaXplKSwgYWxwaGEgPSAuNCkrDQogICBmYWNldF9ncmlkKHJvd3MgPXZhcnMocmlza19kaXN0X3RyYW5zbCkpDQpgYGANCg0KSW4gdGhlIGZvbGxvd2luZywgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlICoqbW9kZSoqIG9mIGVhY2ggZGVuc2l0eSBwbG90ICh0aGF0IGlzLCB3aGVyZSB0aGUgcG9sYXJpemF0aW9uIG1lYXN1cmVzIGNvbnZlcmdlcyBvciBoYXMgYSBoaWdoIHByb2JhYmlsaXR5IHRoYXQgaXQgZW5kcyB1cCBvbiBYLCAqKmlmIHdlIHdlcmUgdG8gc2FtcGxlIGl0IGZld2VyIHRpbWVzKiopLiBXZSBjYW4gdGhlbiBwbG90IHRoZXNlIGZpbmRpbmdzLCBhbmQgc2hvdWxkIGxvb2sgYXQgdGhlIHBvaW50IHdoZXJlIHRoZSAqKm1vZGVzKiogY29udmVyZ2VzIHRvd2FyZHMgdGhlICJ0cnVlIHNjb3JlIiAoaW4gb3VyIGNhc2UsIHRoZSBzYW1wbGUgc2l6ZSBvZiA1MDAwKS4NCg0KYGBge3IsIGZpZy53aWR0aD0xNH0NCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgbXV0YXRlKGdyb3VwZWRfZGlzdFhzYW1wbGUgPSBwYXN0ZShyaXNrX2Rpc3RfdHJhbnNsLCBmc2FtcGxlX3NpemUsIHNlcCA9ICJfIikpDQoNCnNwbGl0dGVkIDwtIHNwbGl0KGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyRwb2xhcml6YXRpb25fcmVzdWx0LCBmID0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzJGdyb3VwZWRfZGlzdFhzYW1wbGUpDQpzcGxpdHRlZDIgPC0gc3BsaXQoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzJEJDX3Jlc3VsdCwgZiA9IGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyRncm91cGVkX2Rpc3RYc2FtcGxlKQ0KDQptb2RlX2RlbnNfcmVzIDwtIGRhdGEuZnJhbWUoKQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoc3BsaXR0ZWQpKSB7DQoNCiAgIGRlbnNfcmVzIDwtIGRlbnNpdHkoc3BsaXR0ZWRbW2ldXSkNCiAgIGRlbnNfcmVzMiA8LSBkZW5zaXR5KHNwbGl0dGVkMltbaV1dKQ0KICAgDQogICBkZW5zX2RmIDwtIGRhdGEuZnJhbWUoZ3JvdXAgPSBuYW1lcyhzcGxpdHRlZClbaV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgbW9kZV9kZW5zX3BvbCA9IGRlbnNfcmVzJHhbd2hpY2gubWF4KGRlbnNfcmVzJHkpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlX2RlbnNfQkMgPSBkZW5zX3JlczIkeFt3aGljaC5tYXgoZGVuc19yZXMyJHkpXSkNCiAgIA0KICAgbW9kZV9kZW5zX3JlcyA8LSByYmluZChtb2RlX2RlbnNfcmVzLCBkZW5zX2RmKQ0KICAgDQp9DQoNCmNsZWFuZWRfbW9kZV9kZW5zX3JlcyA8LSBtb2RlX2RlbnNfcmVzICU+JSANCiAgIHNlcGFyYXRlKGdyb3VwLCBzZXAgPSAiXyIsIGludG8gPSBjKCJEaXN0cmlidXRpb24iLCAiZnNhbXBsZV9zaXplIikpICU+JSANCiAgIG11dGF0ZShTYW1wbGVfU2l6ZSA9IGFzLmludGVnZXIoZnNhbXBsZV9zaXplKSkgJT4lIA0KICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgibW9kZV9kZW5zIiksDQogICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gIm1vZGVfZGVuc18iLA0KICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1lYXN1cmUiLA0KICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpDQogICANCg0KY2xlYW5lZF9tb2RlX2RlbnNfcmVzICU+JSANCiAgIGdncGxvdChhZXMoU2FtcGxlX1NpemUsIHZhbHVlLCBncm91cCA9IERpc3RyaWJ1dGlvbiwgY29sID0gRGlzdHJpYnV0aW9uKSkrDQogICBnZW9tX2xpbmUoc2l6ZT0gMS4xKSsNCiAgIGZhY2V0X2dyaWQocm93cyA9IHZhcnMobWVhc3VyZSkpKw0KICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNhbXBsZV9zZXF1ZW5jZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzKQ0KDQpgYGANCg0KIyMgUHJvYmFiaWxpdHkgb2YgU2FtcGxlIGJlaW5nIGluIFRydWUgU2NvcmUgSW50ZXJ2YWwgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KV2hpbGUgdGhlIG1vZGUgY291bGQgYSBiZSBnb29kIGluZGljYXRvciwgb25lIG1pZ2h0IGFsc28gYXJndWUgdGhhdCBpdCBpcyBtb3JlIHJlbGV2YW50IHRvIGhhdmUgYSBnb29kIGNoYW5jZSBvZiBjYXB0dXJpbmcgdGhlIHRydWUgcG9sYXJpemF0aW9uIHZhbHVlLiBUaGF0IGlzLCB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgbWVhc3VyZSBpcyBvbmx5IG9mZiBieSBvbmx5IC4xIChtZWFuaW5nIGl0IGlzIGFsd2F5cyB3aXRoaW4gYSBpbnRlcnZhbCBvZiArIDAuMDUgYW5kIC0gMC4wNSkgZnJvbSB0aGUgdHJ1ZSBzY29yZS4gU28gbGV0J3MgY2FsY3VsYXRlIHRoZSAlIHdoZXJlIHRoZSBgYHIgcmVwbGljYXRpb25zX3Blcl9zZXR0aW5nYGAgc2ltdWxhdGlvbnMgcGVyIHNhbXBsZSBzaXplIHdhcyB3aXRoaW4gdGhpcyBpbnRlcnZhbC4gT25lIGNvdWxkIHRoZW4gaW50ZXJwcmV0IHRoZSByZXN1bHRzIGFzOiAqKnRoZSBwcm9iYWJpbGl0eSBvZiBjb21pbmcgdG8gdGhlIHNhbWUgcG9sYXJpemF0aW9uIHZhbHVlICh3aXRoIGFuIGludGVydmFsIG9mIC4xKSBjb21wYXJlZCB0byBpZiB3ZSBoYWQgc2FtcGxlZCA1JzAwMCBwZW9wbGUqKi4NCg0KIyMjIEludGVydmFsIG9mIC4xDQoNCmBgYHtyLCBmaWcud2lkdGg9IDE0fQ0KIyBjYWxjdWxhdGUgdGhlICJ0cnVlIHNjb3JlIiB2YWx1ZSBvZiBzYWlkIHBvbGFyaXphdGlvbiBtZWFzdXJlIGZvciBlYWNoIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9uDQoNCnRydWVfc2NvcmUgPC0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGZpbHRlcihzYW1wbGVfc2l6ZSA9PSA1MDAwKSAlPiUgDQogICBncm91cF9ieShyaXNrX2Rpc3RfdHJhbnNsKSAlPiUgDQogICBzdW1tYXJpemUobWVhbl90cnVlX3Njb3JlX0JDID0gbWVhbihCQ19yZXN1bHQpLA0KICAgICAgICAgICAgIG1lYW5fdHJ1ZV9zY29yZV9wb2wgPSBtZWFuKHBvbGFyaXphdGlvbl9yZXN1bHQpKQ0KDQoNCg0KDQpjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXNfd2l0aGluX2ludGVydmFsIDwtIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyAlPiUgDQogICBsZWZ0X2pvaW4odHJ1ZV9zY29yZSwgYnkgPSBjKCJyaXNrX2Rpc3RfdHJhbnNsIikpICU+JSANCiAgIG11dGF0ZSh3aXRoaW5fQkMgPSBpZl9lbHNlKEJDX3Jlc3VsdCA+PSBtZWFuX3RydWVfc2NvcmVfQkMtIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQkNfcmVzdWx0IDw9IG1lYW5fdHJ1ZV9zY29yZV9CQyArIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCwgMSwgMCksDQogICAgICAgICAgd2l0aGluX3BvbCA9IGlmX2Vsc2UocG9sYXJpemF0aW9uX3Jlc3VsdCA+PSBtZWFuX3RydWVfc2NvcmVfcG9sLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvbGFyaXphdGlvbl9yZXN1bHQgPD0gbWVhbl90cnVlX3Njb3JlX3BvbCArIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCwgMSwgMCkpDQoNCnN1bW1hcml6ZWRfd2l0aGluX2ludGVydmFsIDwtDQogICBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXNfd2l0aGluX2ludGVydmFsICU+JQ0KICAgZ3JvdXBfYnkocmlza19kaXN0X3RyYW5zbCwgc2FtcGxlX3NpemUpICU+JQ0KICAgc3VtbWFyaXplKGNvdW50X3dpdGhpbl9CQyA9IHN1bSh3aXRoaW5fQkMpLA0KICAgICAgY291bnRfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSwNCiAgICAgIHBlcmNfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQykgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDAsDQogICAgICBwZXJjX3dpdGhpbl9wb2wgPSBzdW0od2l0aGluX3BvbCkgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDApICU+JQ0KICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgicGVyYyIpLA0KICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsDQogICAgICB2YWx1ZXNfdG8gPSAicGVyY2VudGFnZSIsDQogICAgICBuYW1lc19wcmVmaXggPSAicGVyY193aXRoaW5fIikNCg0Kc3VtbWFyaXplZF93aXRoaW5faW50ZXJ2YWwgJT4lIGdncGxvdChhZXMoZmFjdG9yKHNhbXBsZV9zaXplKSwgcGVyY2VudGFnZSwgY29sID0gcmlza19kaXN0X3RyYW5zbCwgZ3JvdXAgPSByaXNrX2Rpc3RfdHJhbnNsKSkrDQogICBnZW9tX3BvaW50KCkrDQogICBnZW9tX2xpbmUoKSsNCiAgIGZhY2V0X3dyYXAofm1lYXN1cmUpKw0KICAgdGhlbWVfbWluaW1hbCgpKw0KICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSsNCiAgIGxhYnMoeSA9ICJwZXJjZW50YWdlIG9mIHNpbXVsYXRpb25zIHdpdGhpbiAwLjA1IGludGVydmFsIG9mIHRydWUgc2NvcmUiLA0KICAgICAgICB4ID0gInNhbXBsZSBzaXplIiwNCiAgICAgICAgdGl0bGUgPSAiSW50ZXJ2YWwgb2YgKy0gMC4wNSIpKw0KICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykrDQogICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQogICANCg0KYGBgDQoNCiMjIyBJbnRlcnZhbCBvZiAuMDUgey5hY3RpdmV9DQoNCkFzIHRoZSBpbnRlcnZhbCBvZiAuMDUgKHdoaWNoIHJlc3VsdHMgb2YgYSBjb3ZlcmFnZSBvZiAuMSkgd2FzICJhcmJpdHJhcmlseSIgdGFrZW4sIGxldCdzIHVzZSB0aGUgc2FtZSBwcm9jZWR1cmUgZm9yIGFuIGludGVydmFsIG9mIC4wMjUgKG9yIGEgc3BhbiBvZiBvbmx5IDAuMDUpLiBJbiB0aGlzIGNhc2UsIG9uZSB3b3VsZCBleHBlY3QgdGhhdCB3ZSBuZWVkIGEgYmlnZ2VyIHNhbXBsZSBzaXplLCBhcyB0aGUgbWFyZ2luIGZvciAiZXJyb3IiIGlzIHNtYWxsZXIgd2hlcmUgd2Ugd291bGQgYmUgY29uZmlkZW50IGVub3VnaCB0aGF0IHdlIGhhdmUgY2FwdHVyZWQgdGhlIHRydWUgc2NvcmUuDQoNCg0KYGBge3IsIGZpZy53aWR0aD0xNH0NCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgPC0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGxlZnRfam9pbih0cnVlX3Njb3JlLCBieSA9IGMoInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbXV0YXRlKHdpdGhpbl9CQyA9IGlmX2Vsc2UoQkNfcmVzdWx0ID49IG1lYW5fdHJ1ZV9zY29yZV9CQy0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQkNfcmVzdWx0IDw9IG1lYW5fdHJ1ZV9zY29yZV9CQyArIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdDIsIDEsIDApLA0KICAgICAgICAgIHdpdGhpbl9wb2wgPSBpZl9lbHNlKHBvbGFyaXphdGlvbl9yZXN1bHQgPj0gbWVhbl90cnVlX3Njb3JlX3BvbC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9sYXJpemF0aW9uX3Jlc3VsdCA8PSBtZWFuX3RydWVfc2NvcmVfcG9sICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiwgMSwgMCkpDQoNCnN1bW1hcml6ZWRfd2l0aGluX2ludGVydmFsIDwtDQogICBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXNfd2l0aGluX2ludGVydmFsICU+JQ0KICAgZ3JvdXBfYnkocmlza19kaXN0X3RyYW5zbCwgc2FtcGxlX3NpemUpICU+JQ0KICAgc3VtbWFyaXplKGNvdW50X3dpdGhpbl9CQyA9IHN1bSh3aXRoaW5fQkMpLA0KICAgICAgY291bnRfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSwNCiAgICAgIHBlcmNfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQykgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDAsDQogICAgICBwZXJjX3dpdGhpbl9wb2wgPSBzdW0od2l0aGluX3BvbCkgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDApICU+JQ0KICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgicGVyYyIpLA0KICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsDQogICAgICB2YWx1ZXNfdG8gPSAicGVyY2VudGFnZSIsDQogICAgICBuYW1lc19wcmVmaXggPSAicGVyY193aXRoaW5fIikNCg0Kc3VtbWFyaXplZF93aXRoaW5faW50ZXJ2YWwgJT4lIGdncGxvdChhZXMoZmFjdG9yKHNhbXBsZV9zaXplKSwgcGVyY2VudGFnZSwgY29sID0gcmlza19kaXN0X3RyYW5zbCwgZ3JvdXAgPSByaXNrX2Rpc3RfdHJhbnNsKSkrDQogICBnZW9tX3BvaW50KCkrDQogICBnZW9tX2xpbmUoKSsNCiAgIGZhY2V0X3dyYXAofm1lYXN1cmUpKw0KICAgdGhlbWVfbWluaW1hbCgpKw0KICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSsNCiAgIGxhYnMoeSA9ICJwZXJjZW50YWdlIG9mIHNpbXVsYXRpb25zIHdpdGhpbiAwLjAyNSBpbnRlcnZhbCBvZiB0cnVlIHNjb3JlIiwNCiAgICAgICAgeCA9ICJzYW1wbGUgc2l6ZSIsDQogICAgICAgIHRpdGxlID0gIkludGVydmFsIG9mICstIDAuMDI1IikrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzKSsNCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KYGBgDQoNCiMjIERpZmZlcmVuY2UgYmV0d2VlbiB0d28gcG9sYXJpemF0aW9ucyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpUaGUgYXBwcm9hY2ggYWJvdmUgd291bGQgb25seSB0ZWxsIHVzIGhvdyBsaWtlbHkgaXQgaXMgdGhhdCB0aGUgc2FtcGxlZCBkaXN0cmlidXRpb24gd291bGQgbGVhZCB0byBhbG1vc3QgdGhlIHNhbWUgcG9sYXJpemF0aW9uIHZhbHVlIGlmIG9uZSBoYWQgc2FtcGxlZCA1JzAwMCBwZW9wbGUgKG9yIGluIGEgc2Vuc2UsIGEgInRydWUgc2NvcmUiLCBhcyBpdCBjb252ZXJnZXMgdG8gc2FpZCBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbikuIEluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbiwgd2Ugd2lsbCBnbyBvbmUgc3RlcCBmdXJ0aGVyIGFuZCBhbmFseXNlIGhvdyBsaWtlbHkgaXQgaXMgdG8gZmluZCB0aGUgc2FtZSBkaWZmZXJlbmNlIGJldHdlZW4gKip0d28qKiBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbnMgYW5kIHR3byBzYW1wbGVkIGRpc3RyaWJ1dGlvbnMuICANCg0KYGBge3J9DQp0cnVlX3Njb3JlX2xvbmcgPC0gdHJ1ZV9zY29yZSAlPiUgDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJtZWFuX3RydWVfc2NvcmUiKSwNCiAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAibWVhbl90cnVlX3Njb3JlXyIsDQogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsDQogICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikNCg0KDQp0cnVlX3Njb3JlX2V4cGFuZGVkIDwtIGV4cGFuZC5ncmlkKHggPSB0cnVlX3Njb3JlJHJpc2tfZGlzdF90cmFuc2wsIHkgPXRydWVfc2NvcmUkcmlza19kaXN0X3RyYW5zbCkgJT4lIA0KICAgZmlsdGVyKHggIT0geSkgJT4lIA0KICAgZGlzdGluY3QoKSAlPiUgDQogICBsZWZ0X2pvaW4odHJ1ZV9zY29yZSwgYnkgPSBjKCJ4IiA9ICJyaXNrX2Rpc3RfdHJhbnNsIikpICU+JSANCiAgIGxlZnRfam9pbih0cnVlX3Njb3JlLCBieSA9IGMoInkiID0gInJpc2tfZGlzdF90cmFuc2wiKSwgc3VmZml4ID0gYygiX3giLCAiX3kiKSkgJT4lIA0KICAgbXV0YXRlKHRydWVfZGlmZl9CQyA9IG1lYW5fdHJ1ZV9zY29yZV9CQ194IC0gbWVhbl90cnVlX3Njb3JlX0JDX3ksDQogICAgICAgICAgdHJ1ZV9kaWZmX3BvbCA9IG1lYW5fdHJ1ZV9zY29yZV9wb2xfeCAtIG1lYW5fdHJ1ZV9zY29yZV9wb2xfeSkNCg0KDQojYmVjYXVzZSB3ZSBoYXZlIGR1cGxpY2F0ZSBjb21iaW5hdGlvbnMgKGUuZy4gYWJzKHggLSB5KSA9PSBhYnMoeSAtIHgpKSwgSSdsbCBzb3J0IHRoZW0gYW5kIHRha2UgaGFsZiBvZiBpdCAoZS5nLiBvbmx5IHRoZSBuZWNjZXNzYXJ5IGNvbWJpbmF0aW9ucykNCm5fdW5pcXVlIDwtIG5yb3codHJ1ZV9zY29yZSkgKiAobnJvdyh0cnVlX3Njb3JlKS0xKSAvIDINCg0KdHJ1ZV9zY29yZV91bmlxdWUgPC0gdHJ1ZV9zY29yZV9leHBhbmRlZCAlPiUgDQogICBhcnJhbmdlKGRlc2ModHJ1ZV9kaWZmX0JDKSkgJT4lIA0KICAgc2xpY2VfaGVhZChuID0gbl91bmlxdWUpICU+JSANCiAgIHNlbGVjdCh4LCB5LCB0cnVlX2RpZmZfQkMsIHRydWVfZGlmZl9wb2wpDQoNCg0KDQp0cnVlX3Njb3JlX3VuaXF1ZSAlPiUgDQogICBrYWJsZShkaWdpdHMgPSAyKQ0KYGBgDQoNClRoZXNlIGFyZSB0aGUgdHJ1ZSBzY29yZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHR3byBkaXN0cmlidXRpb25zIGlmIG9uZSB3ZXJlIHRvIGF2ZXJhZ2UgdGhlIGRpZmZlcmVuY2UgZnJvbSBzYW1wbGluZyA1MDAwIHJhdGluZ3MgZm9yIGVhY2ggcG9wdWxhdGlvbi4gIA0KDQoNCkluIHRoZSBmb2xsb3dpbmcsIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGNhbGN1bGF0ZWQgcG9sYXJpemF0aW9uIG1lYXN1cmVzIGZyb20gYW5vdGhlciBkaXN0cmlidXRpb24uIEFnYWluLCB3ZSB3aWxsIHRha2UgdHdvIGludGVydmFsczoNCi4xIGFuZCAuMDUgKHRoYXQgaXMsIHRoZSBzYW1wbGVkIGRpZmZlcmVuY2UgaXMgYWxsb3dlZCB0byBkZXZpYXRlIGJ5IC4wNSBvciAuMDI1IGZyb20gdGhlIHRydWUgc2NvcmUgb24gZWl0aGVyIHNpZGUpLg0KDQojIyMgSW50ZXJ2YWwgb2YgLjENCg0KYGBge3IsIGZpZy53aWR0aD0gMTR9DQoNCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc19zZWxlY3RlZCA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgc2VsZWN0KGNvbnRhaW5zKCJyZXN1bHQiKSwgc2FtcGxlX3NpemUsIHJpc2tfZGlzdF90cmFuc2wpICU+JSANCiAgIGdyb3VwX2J5KHJpc2tfZGlzdF90cmFuc2wsIHNhbXBsZV9zaXplKSAlPiUgDQogICBtdXRhdGUoc2FtcGxlX0lEID0gcm93X251bWJlcigpKQ0KDQpkaWZmX2Rpc3RyaWJ1dGlvbnMgPC0gdHJ1ZV9zY29yZV91bmlxdWUgJT4lIA0KICAgbGVmdF9qb2luKGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc19zZWxlY3RlZCwgYnkgPSBjKCJ4IiA9ICJyaXNrX2Rpc3RfdHJhbnNsIikpICU+JSANCiAgIGxlZnRfam9pbihjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXNfc2VsZWN0ZWQsIGJ5ID0gYygieSIgPSAicmlza19kaXN0X3RyYW5zbCIsICJzYW1wbGVfc2l6ZSIsICJzYW1wbGVfSUQiKSwgc3VmZml4ID0gYygiX3giLCAiX3kiKSkgJT4lIA0KICAgbXV0YXRlKGRpZmZfQkMgPSBCQ19yZXN1bHRfeCAtIEJDX3Jlc3VsdF95LA0KICAgICAgICAgIGRpZmZfcG9sID0gcG9sYXJpemF0aW9uX3Jlc3VsdF94IC0gcG9sYXJpemF0aW9uX3Jlc3VsdF95KQ0KDQp3aXRoaW5faW50ZXJ2YWxfZGlmZl9kaXN0cmlidXRpb25zIDwtIGRpZmZfZGlzdHJpYnV0aW9ucyAlPiUgDQogICBtdXRhdGUod2l0aGluX0JDID0gaWZfZWxzZSh0cnVlX2RpZmZfQkMgPj0gZGlmZl9CQyAtIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlX2RpZmZfQkMgPD0gZGlmZl9CQyArIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCwgMSwgMCksDQogICAgICAgICAgd2l0aGluX3BvbCA9IGlmX2Vsc2UodHJ1ZV9kaWZmX3BvbCA+PSBkaWZmX3BvbCAtIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlX2RpZmZfcG9sIDw9IGRpZmZfcG9sICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSwNCiAgICAgICAgICBjb21wYXJpc29uID0gcGFzdGUoeCwgeSwgc2VwID0gIiAtICIpDQogICApICU+JSANCiAgIGdyb3VwX2J5KGNvbXBhcmlzb24sIHNhbXBsZV9zaXplKSAlPiUgDQogICBzdW1tYXJpemUocGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgICAgICAgICBwZXJjX3dpdGhpbl9wb2wgPSBzdW0od2l0aGluX3BvbCkgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDApICU+JSANCiAgIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoInBlcmMiKSwNCiAgICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicGVyY2VudGFnZSIsDQogICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gInBlcmNfd2l0aGluXyIpDQogICANCg0Kd2l0aGluX2ludGVydmFsX2RpZmZfZGlzdHJpYnV0aW9ucyAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSBmYWN0b3IoY29tcGFyaXNvbiksIGdyb3VwID0gZmFjdG9yKGNvbXBhcmlzb24pKSkrDQogICBnZW9tX3BvaW50KHNpemUgPSAyKSsNCiAgIGdlb21fbGluZShsaW5ld2lkdGggPSAxLjIpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDUgaW50ZXJ2YWwgb2YgdHJ1ZSBzY29yZSIsDQogICAgICAgIHggPSAic2FtcGxlIHNpemUiLA0KICAgICAgICB0aXRsZSA9ICJJbnRlcnZhbCBvZiArLSAwLjA1IiwNCiAgICAgICAgY29sb3IgPSAiQ29tcGFyaXNvbiBiZXR3ZWVuIikrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzMikrDQogICB0aGVtZShsZWdlbmQuc3BhY2luZyA9IHVuaXQoMC4xLCAiY20iKSwNCiAgICAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oMCwwLDAsMCksDQogICAgICAgICBsZWdlbmQuYm94Lm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwNCiAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjUsLjA1KSwNCiAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpDQogICANCg0KDQpgYGANCg0KIyMjIEludGVydmFsIG9mIC4wNSB7LmFjdGl2ZX0NCmBgYHtyLCBmaWcud2lkdGg9IDE0fQ0KDQp3aXRoaW5faW50ZXJ2YWxfZGlmZl9kaXN0cmlidXRpb25zIDwtIGRpZmZfZGlzdHJpYnV0aW9ucyAlPiUgDQogICBtdXRhdGUod2l0aGluX0JDID0gaWZfZWxzZSh0cnVlX2RpZmZfQkMgPj0gZGlmZl9CQyAtIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdDIgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZV9kaWZmX0JDIDw9IGRpZmZfQkMgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyLCAxLCAwKSwNCiAgICAgICAgICB3aXRoaW5fcG9sID0gaWZfZWxzZSh0cnVlX2RpZmZfcG9sID49IGRpZmZfcG9sIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlX2RpZmZfcG9sIDw9IGRpZmZfcG9sICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiwgMSwgMCksDQogICAgICAgICAgY29tcGFyaXNvbiA9IHBhc3RlKHgsIHksIHNlcCA9ICIgLSAiKQ0KICAgKSAlPiUgDQogICBncm91cF9ieShjb21wYXJpc29uLCBzYW1wbGVfc2l6ZSkgJT4lIA0KICAgc3VtbWFyaXplKHBlcmNfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQykgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDAsDQogICAgICAgICAgICAgcGVyY193aXRoaW5fcG9sID0gc3VtKHdpdGhpbl9wb2wpIC8gcmVwbGljYXRpb25zX3Blcl9zZXR0aW5nICogMTAwKSAlPiUgDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJwZXJjIiksDQogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsDQogICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInBlcmNlbnRhZ2UiLA0KICAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJwZXJjX3dpdGhpbl8iKQ0KICAgDQoNCndpdGhpbl9pbnRlcnZhbF9kaWZmX2Rpc3RyaWJ1dGlvbnMgJT4lIGdncGxvdChhZXMoZmFjdG9yKHNhbXBsZV9zaXplKSwgcGVyY2VudGFnZSwgY29sID0gZmFjdG9yKGNvbXBhcmlzb24pLCBncm91cCA9IGZhY3Rvcihjb21wYXJpc29uKSkpKw0KICAgZ2VvbV9wb2ludChzaXplID0gMikrDQogICBnZW9tX2xpbmUobGluZXdpZHRoID0gMS4yKSsNCiAgIGZhY2V0X3dyYXAofm1lYXN1cmUpKw0KICAgdGhlbWVfbWluaW1hbCgpKw0KICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSsNCiAgIGxhYnMoeSA9ICJwZXJjZW50YWdlIG9mIHNpbXVsYXRpb25zIHdpdGhpbiAwLjAyNSBpbnRlcnZhbCBvZiB0cnVlIHNjb3JlIiwNCiAgICAgICAgeCA9ICJzYW1wbGUgc2l6ZSIsDQogICAgICAgIHRpdGxlID0gIkludGVydmFsIG9mICstIDAuMDI1IiwNCiAgICAgICAgY29sb3IgPSAiQ29tcGFyaXNvbiBiZXR3ZWVuIikrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzMikrDQogICB0aGVtZShsZWdlbmQuc3BhY2luZyA9IHVuaXQoMC4xLCAiY20iKSwNCiAgICAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oMCwwLDAsMCksDQogICAgICAgICBsZWdlbmQuYm94Lm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwNCiAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjUsLjA1KSwNCiAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpDQpgYGANCg0KIyBEaXNjdXNzaW9uDQoNCkFzIG9uZSBoYXMgZXhwZWN0ZWQsIHRoZSAibmVlZGVkIiBzYW1wbGUgc2l6ZSBpbmNyZWFzZXMgdGhlIHN0cmljdGVyIHdlIHNldCB0aGUgaW50ZXJ2YWxzIHdoaWNoIHdlIHdvdWxkIGJlIHNhdGlzZmllZCB3aXRoLiAgDQoNCklmIG9uZSB3ZXJlIHRvIG9ubHkgbG9vayBhdCB0aGUgcGxvdHMgd2l0aCB0aGUgc3RyaWN0ZXIgaW50ZXJ2YWw6DQoNCi0gT24gdGhlIHNpZGUgb2YgYmltb2RhbGl0eSBjb2VmZmljaWVudCwgb25lIGNvdWxkIGFyZ3VlIHRvIHNhbXBsZSBhcm91bmQgMTI1MCBwYXJ0aWNpcGFudHMgc28gdGhhdCB3ZSBoYXZlIGEgZ29vZCBjaGFuY2Ugb2YgYXJvdW5kIDgwJSB0byBnZXQgYSBnb29kIGVzdGltYXRlIGZvciBbKipyYXJlIGRpc3RyaWJ1dGlvbnMqKl17c3R5bGU9ImNvbG9yOiBgciBjb2xvcnNbM11gOyJ9IChzbyB0aGF0IGluIGNhc2Ugd2UgZG8gc2FtcGxlIGZyb20gYSByYXJlIGRpc3RyaWJ1dGlvbiwgaW4gODAlIG9mIHRoZSB0aW1lLCB0aGUgdHJ1ZSBiaW1vZGFsaXR5IGNvZWZmaWNpZW50IGlzIG9mZiBieSBvbmx5IGEgc21hbGwgYW1vdW50KS4gSWYgb25lIHdhbnRzIHRvIG1ha2Ugc3VyZSwgZXZlbiBzYW1wbGluZyAyMDAwIHBhcnRpY2lwYW50cyB3b3VsZCBiZSBuaWNlIHRvIGluY3JlYXNlIHRoZSBjaGFuY2UgdG8gOTAlLCBidXQgbWF5YmUgdGhhdCBpcyBzdHJldGNoaW5nIHRoZSBidWRnZXQgYSBsaXR0bGUgYml0Li4uDQoNCi0gRm9yIHRoZSBtZWFzdXJlIG9mIHBvbGFyaXphdGlvbiwgZXN0aW1hdGluZyB0aGUgdHJ1ZSBzY29yZSB2YWx1ZSBpcyBoYXJkZXN0IGlmIG9uZSBoYXMgYSBbKipzdHJvbmdseSBwb2xhcml6ZWQqKl17c3R5bGU9ImNvbG9yOiBgciBjb2xvcnNbNF1gOyJ9IGRpc3RyaWJ1dGlvbi4gSW4gdGhpcyBjYXNlLCB3ZSBoYXZlIGEgODAlIGNoYW5jZSBvZiBnZXR0aW5nIGl0ICJyaWdodCIgaWYgb25lIHdvdWxkIHNhbXBsZSAxMjUwIHBhcnRpY2lwYW50cywgb3IgOTAlIGZvciBzYW1wbGluZyAyMDAwIHJlc3BlY3RpdmVseS4NCg0KVGhlIG51bWJlcnMgaGVyZSBhcmUgb25seSBzdWdnZXN0aW9ucywgYW5kIGFyZSBvbmx5IGJhc2VkIG9uICoqcHJlY2lzaW9uKiosIHNvIHRoYXQgd2UgY2FuIGJlIHN1cmUgdGhhdCB0aGUgcmVzdWx0cyB3ZSBnZXQgYXJlIHJlbGlhYmxlIGVub3VnaC4gSW4gYW55IGNhc2UsIHRoZXNlIG51bWJlcnMgYXJlICJvbmx5IiB3b3JzdCBjYXNlIHNjZW5hcmlvcywgYXMgdGhlIG90aGVyIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9ucyBhcmUgY29udmVyZ2luZyBlYXJsaWVyLiBXZSBhcmUgYWxzbyBvbmx5IGxvb2tpbmcgYXQgdGhlIHN0cmljdGVyIHNjZW5hcmlvLi4uDQoNCldoZW4gbG9va2luZyBmb3IgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0d28gZGlzdHJpYnV0aW9ucywgb25lIG5lZWQgdG8gc2FtcGxlIDE1MDAgb3IgZXZlbiAyNzUwIHBlb3BsZSBmb3IgODAlIG9yIDkwJSBjaGFuY2Ugb2YgYmVpbmcgbmVhciB0aGUgdHJ1ZSBzY29yZS4gIA0KDQoNCiMjIyBMaW1pdGF0aW9ucw0KDQotIFRoaXMgd29yayB3YXMgZG9uZSBpbiBhIGh1cnJ5LCBhcyB0aGUgbWVudGFsIHN0YXRlIG9mIHRoZSBhdXRob3IgaXMgaW4gc2hhbWJsZXMuDQotIERpc2N1c3Npb24gd2FzIHNvbGVseSBiYXNlZCBvbiB0aGUgc3RyaWN0ZXIgaW50ZXJ2YWwgKGUuZy4gdGhlIGxhc3QgcGxvdCksIG5vdCBvbiB0aGUgbGVuaWVudCBvbmUgd2l0aCBhIGJyb2FkZXIgaW50ZXJ2YWwuDQotIEFzIGFsd2F5cywgdGhlIHNpbXVsYXRpb24gc2hvdWxkIGJlIHRha2VuIHdpdGggYSBncmFpbiBvZiBzYWx0LCBhcyB0aGVzZSByZXN1bHRzIGFyZSBtYWRlIHdpdGggdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgZGlzdHJpYnV0aW9uIGlzIGFzICJiZWF1dGlmdWwiIGFzIEkgbWFkZSBpdCBpbiB0aGUgaW50cm9kdWN0aW9uIHNlY3Rpb24uLi4gDQotIFdoZXRoZXIgd2UgY2FuIHRydWx5IHNhbXBsZSBhcyByYW5kb20gYXMgd2Ugd2FudCB0bywgb3Igd2UgZ2V0IHNvbWUgc3Vic2FtcGxlcyBpcyBub3Qgb2YgZGViYXRlIGhlcmUuIFRoaXMgd29yayBqdXN0IGFzc3VtZXMgdGhhdCB0aGUgd2hvbGUgcG9wdWxhdGlvbiBoYXMgWCBkaXN0cmlidXRpb24uDQotIE5vIGNvbXBhcmlzb24gYmV0d2VlbiBkaWZmZXJlbnQgc2FtcGxlIGRpc3RyaWJ1dGlvbnMgd2VyZSBtYWRlIChlLmcuIG92ZXJsYXAgY29lZmZpY2llbnQpLCBzbyB3ZSBzdGlsbCBoYXZlIG5vIGlkZWEgaG93IGJpZyB0aGUgc2FtcGxlIHNpemUgbmVlZHMgdG8gYmUgaWYgd2Ugd2FudCB0byBkaXNjb3ZlciBhIGRpZmZlcmVuY2Ugb2YgWCBiZXR3ZWVuIHR3byBjb3VudHJpZXMvIGdyb3VwcyAqKmluIGNhc2UgdGhlcmUgaXMgYSBkaWZmZXJlbmNlKiouDQoNCiMjIyBDcmVkaXRzDQoNCldJUA0K